﻿using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Web.Mvc;
using Citi.Scheduler.Domain;
using Citi.Scheduler.WebUi.Models;
using Citi.Helpers;

namespace Citi.Scheduler.WebUi.Controllers
{
    public class RoutineController : ControllerBase<RoutineModel, Routine>
    {
        public ActionResult Index()
        {
            return IndexBase(true);
        }

        public ActionResult Form()
        {
            return View();
        }

        public ActionResult RoutineHierarchy(int id)
        {
            ViewBag.CurrentRoutineId = id;
            var routines = new Routine().SelectById<Routine>(id).GetAllHierarchy();
            return PartialView(new RoutineModel().CreateModel(routines));
        }

        [OutputCache(NoStore = true, Duration = 0, VaryByParam = "*")]
        public ActionResult Calendar(int routineId = 0, int categoryId = 0)
        {
            ViewBag.RoutineId = routineId;
            ViewBag.CategoryId = categoryId;
            ViewBag.Name = categoryId != 0
                               ? new Category().SelectById<Category>(categoryId).Name
                               : new Routine().SelectById<Routine>(routineId).Name;

            return View();
        }

        [OutputCache(NoStore = true, Duration = 0, VaryByParam = "*")]
        public JsonResult FillCalendar(int month, int year, int routineId = 0, int categoryId = 0)
        {
            var executions = new List<RoutineExecution>();
            var currentDate = new DateTime(year, month, 1);

            if (month <= DateTime.Today.Month && year <= DateTime.Today.Year)
                executions = new RoutineExecution().GetByDate(currentDate, routineId, categoryId).ToList();

            var eventList = executions.Select(e =>
                                           new
                                           {
                                               id = e.Id,
                                               title = e.Routine.Name,
                                               status = (e.RoutineExecutionHistories.Any() ? (RoutineExecutionStatus)e.RoutineExecutionHistories.Last().Status : RoutineExecutionStatus.Scheduled).ToString(),
                                               start = e.StartsOn.HasValue && e.EndsOn.HasValue ? e.StartsOn.Value.ToString("s") : e.ScheduledDateTime.ToString("s"),
                                               end = e.StartsOn.HasValue && e.EndsOn.HasValue ? e.EndsOn.Value.ToString("s") : e.ScheduledDateTime.ToString("s"),
                                               color = "#FF0000"
                                           }).ToList();

            new RoutineSchedule().GetExecutionScheduled(new DateTime(year, month, 1), routineId, categoryId).ToList().ForEach(x =>
                    {
                        var inicialDate = month == DateTime.Today.Month && year == DateTime.Today.Year
                                                ? DateTime.Today
                                                : new DateTime(year, month, 1);

                        var finalDate = new DateTime(year, month, 1).AddMonths(1).AddHours(-1);

                        for (int i = 0; i < (finalDate - inicialDate).TotalDays; i++)
                        {
                            currentDate = new DateTime(year, month, i + 1);

                            if (!RoutineSchedule.RunOnDate(x.RoutineSchedules.Last().Schedule, currentDate)
                                || executions.Any(y => x.Id == y.RoutineId && y.ScheduledDateTime.Date == currentDate.Add(x.RoutineSchedules.Last().Schedule.Time.TimeOfDay).Date))
                                continue;

                            var scheduledDateTime = currentDate.Add(x.RoutineSchedules.Last().Schedule.Time.TimeOfDay).Date;

                            eventList.Add(new
                                {
                                    id = default(int),
                                    title = x.Name,
                                    status = RoutineExecutionStatus.Scheduled.ToString(),
                                    //className = "testEvent",
                                    start = scheduledDateTime.ToString("s"),
                                    end = scheduledDateTime.ToString("s"),
                                    color = "#FF0000"
                                });
                        }
                    });

            return Json(eventList.ToArray(), JsonRequestBehavior.AllowGet);
        }

        [OutputCache(NoStore = true, Duration = 0, VaryByParam = "*")]
        public ActionResult ActionForm(int? id)
        {
            return ActionBase(id, true);
        }

        [HttpPost]
        public ActionResult ActionForm(RoutineModel model)
        {
            model.UseLastVersion = model.LibraryVersionId == -1;
            if (model.PreferredMachineId == -1) model.PreferredMachineId = null;
            if (model.PredecessorRoutineId == -1) model.PredecessorRoutineId = null;
            if (model.LibraryVersionId == -1) model.LibraryVersionId = new LibraryVersion().GetLastByLibraryIdAndType(model.LibraryId, model.Type).Id;
            model.Active = true;

            var action = ActionBase(model, false);

            if (model.Id != 0)
            {
                var escalationSaved = new Escalation().SelectByRoutine(Model.Id).LastOrDefault();

                if (escalationSaved != null)
                {
                    escalationSaved.UserId = Model.ResponsibleId;
                    escalationSaved.Save(escalationSaved);

                    return RedirectToAction("Form");
                }
            }

            var escalation = new Escalation { UserId = Model.ResponsibleId, RoutineId = Model.Id, Minutes = 0 };
            escalation.Save(escalation);

            return RedirectToAction("Form");
        }

        [HttpPost]
        public ActionResult AssociateSchedule(int id)
        {
            return IndexBase(true);
        }

        public ActionResult Delete(int id)
        {
            return DeleteBase(id, true);
        }

        [AcceptVerbs(HttpVerbs.Get)]
        [OutputCache(NoStore = true, Duration = 0, VaryByParam = "*")]
        public JsonResult GetVersion(int libraryId, int type)
        {
            var versions = GetVersionsModel(libraryId, type);
            return Json(versions, JsonRequestBehavior.AllowGet);
        }

        [AcceptVerbs(HttpVerbs.Get)]
        [OutputCache(NoStore = true, Duration = 0, VaryByParam = "*")]
        public JsonResult GetCategoriesByType(LibraryType type)
        {
            var categories = new CategoryModel().CreateModel(new Category().GetCategoriesByLibraryType(type)).OrderBy(x => x.Name).ToList();
            categories.Insert(0, new CategoryModel { Id = -1, Name = categories.Count == 0 ? "There's no category" : "Select a Category" });
            return Json(categories, JsonRequestBehavior.AllowGet);
        }

        [AcceptVerbs(HttpVerbs.Get)]
        [OutputCache(NoStore = true, Duration = 0, VaryByParam = "*")]
        public JsonResult GetLibrariesByCategoryAndType(int categoryId, LibraryType type)
        {
            var libraries = new LibraryModel().CreateModel(new Library().GetLibrariesByCategoryAndType(categoryId, type)).OrderBy(x => x.Name).ToList();
            libraries.Insert(0, new LibraryModel { Id = -1, Name = libraries.Count == 0 ? "There's no Librart" : "Select a Library" });
            return Json(libraries, JsonRequestBehavior.AllowGet);
        }

        [AcceptVerbs(HttpVerbs.Get)]
        [OutputCache(NoStore = true, Duration = 0, VaryByParam = "*")]
        public JsonResult GetLibrariesByType(LibraryType type)
        {
            var libraries = new LibraryModel().CreateModel(new Library().GetLibrariesByType(type)).OrderBy(x => x.Name).ToList();
            libraries.Insert(0, new LibraryModel { Id = -1, Name = libraries.Count == 0 ? "There's no Librart" : "Select a Library" });
            return Json(libraries, JsonRequestBehavior.AllowGet);
        }

        private List<LibraryVersionModel> GetVersionsModel(int libraryId, int type)
        {
            var versions = new LibraryVersionModel().CreateModel(new LibraryVersion().GetValidsByLibraryIdAndType(libraryId, type)).ToList();
            versions.Insert(0, new LibraryVersionModel { Id = -1, Version = "Last version" });
            return versions;
        }

        [AcceptVerbs(HttpVerbs.Get)]
        [OutputCache(NoStore = true, Duration = 0, VaryByParam = "*")]
        public JsonResult GetRoutinesByLibrary(int libraryId, int type, int id = 0)
        {
            var routines = GetRoutinesModel(libraryId, type, id);
            return Json(routines, JsonRequestBehavior.AllowGet);
        }

        private static List<RoutineModel> GetRoutinesModel(int libraryId, int type, int id)
        {
            var routines = new RoutineModel().CreateModel(new Routine().GetRoutinesByLibraryIdAndType(libraryId, type)).Where(x => x.Id != id).OrderBy(x => x.Name).ToList();
            routines.Insert(0, new RoutineModel { Id = -1, Name = "Select a predecessor routine" });
            return routines;
        }

        [AcceptVerbs(HttpVerbs.Get)]
        [OutputCache(NoStore = true, Duration = 0, VaryByParam = "*")]
        public JsonResult GetClass(int libraryVersionId)
        {
            var classes = GetClassesFromLibrary(libraryVersionId);
            return Json(classes.Select(x => new { ClassName = x.Name }), JsonRequestBehavior.AllowGet);
        }

        private IEnumerable<Type> GetClassesFromLibrary(int libraryVersionId)
        {
            var version = new LibraryVersion().SelectById<LibraryVersion>(libraryVersionId);

            using (NetworkShareAccesser.Access(ServerName, NetworkUser, NetworkPassword))
            {
                var pathLibrary = Path.Combine(DllFolder, version.LibraryId.ToString(), ((LibraryType)version.Type).ToString(), version.Version, version.LibraryFile);
                var dll = System.Reflection.Assembly.LoadFile(pathLibrary);
                return dll.GetExportedTypes().Where(x => RoutineBaseTypes.Any(y => y.IsAssignableFrom(x))).OrderBy(x => x.Name);
            }
        }

        public void SaveSchedule(ScheduleModel model, int idRoutine, string repeat)
        {
            var idSchedule = new ScheduleController().Save(model, repeat);

            var routineSchedule = new RoutineSchedule { Date = DateTime.Now, RoutineId = idRoutine, ScheduleId = idSchedule };
            routineSchedule.Save(routineSchedule);

            //return IndexBase(true);
        }

        public void FillViewBags(int? responsibleId = null, int? categoryId = null, int? libraryId = null, int? machineId = null,
            int? libraryVersionId = null, int? type = null, bool useLastVersion = false, string @class = null, int? predecessorLibraryId = null, int? id = null)
        {
            ViewBag.Types = new LibraryType().ToSelectList(true, true);

            if (!type.HasValue) type = int.Parse(ViewBag.Types[0].Value);

            //var categories = new Category().GetCategoriesByLibraryType((LibraryType)type).OrderBy(m => m.Description);
            var categories = new Category().SelectAllActive<Category>().OrderBy(m => m.Description);
            ViewBag.Categories = categories.ToSelectList(x => x.Name, x => x.Id.ToString(), "Select a category",
                categoryId.HasValue ? categoryId.Value.ToString() : string.Empty);

            var users = new User().SelectAllActive<User>().OrderBy(m => m.Name);
            ViewBag.Responsibles = users.ToSelectList(x => x.Name, x => x.Id.ToString(), "Select a responsible",
                (responsibleId.HasValue && responsibleId.Value > 0 ? responsibleId.Value : ((Citi.Scheduler.Domain.User)Session["LoggedUser"]).Id).ToString());

            //var libraries = new Library().SelectAllActive<Library>().OrderBy(m => m.Name).ToList();
            var libraries = new Library().GetLibrariesByType((LibraryType)type).OrderBy(m => m.Name).ToList();
            libraries.Insert(0, new Library { Id = -1, Name = "Select a library" });
            ViewBag.Libraries = libraries.ToSelectList(x => x.Name, x => x.Id.ToString());

            var machines = new Machine().SelectByTypeCenter(MachineCenter.Execution).OrderBy(m => m.HostName);
            ViewBag.Machines = machines.ToSelectList(x => x.HostName, x => x.Id.ToString(), "Select a machine",
                machineId.HasValue ? machineId.Value.ToString() : string.Empty);

            ViewBag.Classes = libraryVersionId.HasValue && libraryVersionId > 0
                ? GetClassesFromLibrary(libraryVersionId.Value).Select(x => new SelectListItem { Text = x.Name, Value = x.Name })
                : new List<SelectListItem> { new SelectListItem { Text = "Select a library", Value = "-2" } };

            ViewBag.Versions = libraryId.HasValue && libraryId > 0
                ? GetVersionsModel(libraryId.Value, type.Value).Select(x => new SelectListItem { Text = x.Version, Value = x.Id.ToString() })
                : new List<SelectListItem> { new SelectListItem { Text = "Select a library", Value = "-2" } };

            //Predecessor Libraries
            var predecessorLibraries = new Library().GetLibrariesByType((LibraryType)type).OrderBy(m => m.Name).ToList();
            predecessorLibraries.Insert(0, new Library { Id = -1, Name = "Select a library" });
            ViewBag.PredecessorLibraries = predecessorLibraries.ToSelectList(x => x.Name, x => x.Id.ToString());

            ViewBag.Predecessors = predecessorLibraryId.HasValue && predecessorLibraryId > 0
                ? GetRoutinesModel(predecessorLibraryId.Value, type.Value, id.Value).Select(x => new SelectListItem { Text = x.Name, Value = x.Id.ToString() })
                : new List<SelectListItem> { new SelectListItem { Text = "Select a library", Value = "-1" } };
        }
    }
}
